using System.Collections.Generic;
using System.IO;
using System.Net;
using Beaker.OpenCube.Anvil;
using Beaker.OpenCube.Game;
using Beaker.OpenCube.Mapping;
using Beaker.OpenCube.Nbt;
using Beaker.Services.Interfaces;
using Beaker.Services.Locator;
using System;
using System.Diagnostics.Contracts;

namespace Beaker.OpenCube
{
	/// <summary>
	/// 
	/// </summary>
	public sealed class DynamicDimension
	{
		private readonly IServiceLocator _serviceLocator;
		private readonly IIOService _ioService;
		private readonly string _dimensionFolderPath;

		/// <summary>
		/// Initializes a new instance of the <see cref="DynamicDimension"/> class.
		/// </summary>
		/// <param name="serviceLocator">The service locator.</param>
		/// <param name="dimensionFolderPath">The folder path of the dimension.</param>
		/// <exception cref="System.ArgumentException">The service locator did not provided an implementation for IIOService;serviceLocator</exception>
		internal DynamicDimension( IServiceLocator serviceLocator, string dimensionFolderPath )
		{
			Contract.Requires<ArgumentNullException>( !string.IsNullOrEmpty( dimensionFolderPath ) );

			_serviceLocator = serviceLocator ?? ServiceLocator.Current ?? PreInitializedServiceLocator.Initialize();
			_ioService = _serviceLocator.GetInstance<IIOService>();

			if( _ioService == null )
				throw new ArgumentException( "The service locator did not provided an implementation for IIOService", "serviceLocator" );

			_dimensionFolderPath = dimensionFolderPath;
		}

		[ContractInvariantMethod]
		private void ContractInvariant()
		{
			Contract.Invariant( _serviceLocator != null );
			Contract.Invariant( _ioService != null );
			Contract.Invariant( !string.IsNullOrEmpty( _dimensionFolderPath ) );
		}


		/// <summary>
		/// Gets a value indicating whether this dimension has chunks.
		/// </summary>
		/// <value>
		/// <c>true</c> if this dimension has chunks; otherwise, <c>false</c>.
		/// </value>
		public bool HasChunks
		{
			get { return true; }
		}

		/// <summary>
		/// Gets the chunk at the given coordinates.
		/// </summary>
		/// <param name="x">The x coordinate of the chunk.</param>
		/// <param name="z">The z coordinate of the chunk.</param>
		/// <returns>The chunk at the given coordinates, or <c>null</c> when there is no chunk generated at the given coordinates.</returns>
		public Chunk GetChunk( int x, int z )
		{
			// determine file of chunk
			string fileName = RegionReader.GetChunkFileName( x, z );
			string filePath = Path.Combine( _dimensionFolderPath, fileName );

			// when the file does not exists, return null (i.e., chunk is not generated)
			if( !File.Exists( filePath ))
				return null;

			using( Stream stream = File.OpenRead( filePath ) )
			{
				NbtDictionary chunkDataDict = RegionReader.ReadChunk( stream, x, z );
				if( chunkDataDict != null && chunkDataDict.ContainsKey( "Level" ) )
				{
					var chunkLevelDataDict = chunkDataDict["Level"] as IDictionary<string, object>;
					if( chunkLevelDataDict != null )
					{
						var mapper = new Mapper<Chunk>();
						var chunk = mapper.MapDictionary( chunkLevelDataDict );

						return chunk;
					}
				}
			}

			return null;
		}
	}
}